#include <iostream>

using namespace std;

// 1. C语言中NULL：用来表示空指针
// #define NULL ((void *)0) // C语言中的NULL
// 也就是说 NULL其实是一个void* 的指针, 在定义指针时，会把void* 隐式转换为相应的类型
// 但是换做一个C++编译器来编译的话是要出错的，因为C++是强类型的，void *是不能隐式转换成其他指针类型的
void test_c_null()
{
    int *i = NULL; // void* 转换为int*  隐式类型转换
}
// 2. C++中的NULL（0）：
//  因为C++不能void* 隐式类型转换为其他，所以C++引入把NULL定义为0， 表示空指针。
//  但是用NULL标准空指针会发生问题？？？
//   例子：定义一个函数

void bar(int a, int *b)
{
    cout << "bar(int a, int *b)" << endl;
}
void bar(int a, int b)
{
    cout << "bar(int a, int b)" << b << endl;
}

void test_cplusplus_oder() // C++中的0
{
    int a = 10;
    int *b = &a;
    bar(a, b); // a:10 b: &a
    // bar(a, NULL); // a:10 b:0   这里都没有问题，那么我现在需要函数重载,函数重载之后，这里就报错了
    // 这里我们希望bar(a, NULL) 匹配的是第一个函数，但是编译器认为NULL是0 直接匹配第二个函数了，所以会产生歧义
    // 发生这种问题的原因：因为g++中的NULL头文件是  #define NULL 0 或者 #define NULL 0L  所以预编译阶段，发生了宏替换，导致编译器找不到当前类型的函数了
}

// 3. C++中的nullptr:
//       nullptr 就是为了解决这个问题，在C++11中的nullptr就很好的解决了
//       nullptr怎么实现的？？
//          1)  首先声明一个my_nullptr类
const class my_nullptr
{
public:
    //允许 my_nullptr 转换为任何类型的指针 T*
    template <class T>
    inline operator T *() const
    {
        return 0;
    }

    // 允许 my_nullptr 转换为类成员指针 T C::*
    template <class C, class T>
    inline operator T C::*() const
    {
        return 0;
    }

private:
    void operator&() const; // 禁止nullptr取地址操作
} my_nullptr = {};          // 表示使用默认构造函数初始化

// 4. c++中 void* → T*	❌	需要 static_cast
//      不同类型指针（int* → float*）	❌	需 reinterpret_cast，不安全
//      有继承关系的类之间: 子类的指针可以给父类

void bar_cplus(int a, int *b)
{
    cout << " bar_cplus(int a, int *b) " << endl;
}
void bar_cplus(int a, int b)
{
    cout << " bar_cplus(int a, int b) " << endl;
}

void test_cplusplus_nullptr()
{
    int a = 10;
    int *b = &a;
    bar_cplus(a, b);
    bar_cplus(a, nullptr); // 这里也是匹配的bar_cplus(int a, int *b) 不是向上面匹配的 bar_cplus(int a, int b)这个版本
}

int main()
{
    // test_c_null();
    // test_cplusplus_oder();
    test_cplusplus_nullptr();
    cout << "aaa" << endl;
    return 0;
}